package org.dromara.solonplugins.activerecord.integration;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.sql.DataSource;

import org.noear.solon.Utils;
import org.noear.solon.core.AppClassLoader;
import org.noear.solon.core.BeanWrap;
import org.noear.solon.core.Props;
import org.noear.solon.core.event.EventBus;
import org.noear.solon.core.util.ClassUtil;

import com.jfinal.kit.StrKit;
import com.jfinal.plugin.IPlugin;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.activerecord.DbKit;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.TableBuilder;
import com.jfinal.plugin.activerecord.cache.EhCache;
import com.jfinal.plugin.activerecord.cache.ICache;
import com.jfinal.plugin.activerecord.dialect.AnsiSqlDialect;
import com.jfinal.plugin.activerecord.dialect.Dialect;
import com.jfinal.plugin.activerecord.dialect.H2Dialect;
import com.jfinal.plugin.activerecord.dialect.InformixDialect;
import com.jfinal.plugin.activerecord.dialect.MysqlDialect;
import com.jfinal.plugin.activerecord.dialect.OracleDialect;
import com.jfinal.plugin.activerecord.dialect.PostgreSqlDialect;
import com.jfinal.plugin.activerecord.dialect.SqlServerDialect;
import com.jfinal.plugin.activerecord.dialect.Sqlite3Dialect;

/**
 * activerecord 配置项 管理工具
 * 
 * @author xls-web (小xu中年)
 * @since 2023-11-01
 * */
public class DbManager {
	
	private static final String TAG = "jfinal.activerecord";

    private static final String ATTR_dialect = "dialect";//数据库方言
    private static final String ATTR_devMode = "devMode"; //开发调试模式
    private static final String ATTR_showSql = "showSql"; //是否打印sql
    private static final String ATTR_txLevel = "txLevel"; //事务控制级别
    private static final String ATTR_tableBuilder = "tableBuilder";//TableBuilder 实现类
    private static final String ATTR_cache = "cache";//缓存类
    /**
     * sql 模板文件，实际开发时将 sql 文件放在 src/main/resources 下，建议使用相对路径
     */
    private static final String ATTR_template = "template";//sqlTemplate
    private static final String ATTR_modelPackage = "modelPackage";//继承jfinal model
    private static final String ATTR_modelClasses ="modelClasses";//在modelPackage继承jfinal model的javabean用#号分隔
    
    private static final Map<String, IPlugin> cached = new ConcurrentHashMap<>();
    
    /**
     * 获取管理器
     * @return 暂时默认是ActiveRecordPlugin类型
     */
    public static IPlugin get(BeanWrap dsWrap) {
        if (dsWrap == null) {
            return null;
        }

        IPlugin tmp = cached.get(dsWrap.name());
        if (tmp == null) {
            synchronized (dsWrap.name().intern()) {
                tmp = cached.get(dsWrap.name());
                if (tmp == null) {
                    tmp = build(dsWrap);
                    cached.put(dsWrap.name(), tmp);
                }
            }
        }
        
        
        return tmp;
    }
    
    /**
     * 注册管理器并启动ActiveRecordPlugin
     */
    public static void reg(BeanWrap bw) {
    	IPlugin iplugin = get(bw);
    	if(iplugin!=null) {
    		iplugin.start();//启动 ActiveRecordPlugin
    	}
    }
    
    /**
     * 停止 ActiveRecordPlugin 实例
     * */
    public static void stop() throws Throwable {
        for (IPlugin arp : cached.values()) {
            arp.stop();
        }
    } 
    
    /**
     * 清空缓存
     */
    public static void clear() {
    	cached.clear();
    }
    
    /**
     * 构建
     */
    private static IPlugin build(BeanWrap bw) {
        DataSource master = bw.raw();
        Props dsProps;

        if (Utils.isNotEmpty(bw.name())) {
            dsProps = bw.context().cfg().getProp(TAG + "." + bw.name());
        } else {
            dsProps = new Props();
        }
        //事务级别控制
        int txLevel = dsProps.getInt(ATTR_txLevel, DbKit.DEFAULT_TRANSACTION_LEVEL);
        dsProps.remove(ATTR_txLevel);
        //as bean name
        String dataSourceId = (bw.name() == null ? DbKit.MAIN_CONFIG_NAME : bw.name());//默认是main
        ActiveRecordPlugin arp = new ActiveRecordPlugin(dataSourceId,master,txLevel);
        boolean isDev = dsProps.getBool(ATTR_devMode, false);
        boolean isShow = dsProps.getBool(ATTR_showSql, true);//默认输出执行sql
        arp.setDevMode(isDev);
        arp.setShowSql(isShow);
        dsProps.remove(ATTR_devMode);
        dsProps.remove(ATTR_showSql);
        
        List<String> sqlTemplates = dsProps.getList(ATTR_template);
        dsProps.remove(ATTR_template);
        
        //添加activerecord sql文件
        if(sqlTemplates != null && sqlTemplates.size() > 0) {
        	for(String s:sqlTemplates) {
        		arp.addSqlTemplate(s);
        	}
        }
        //开始扫描并加载activerecord model javabean
        String modelPackage = dsProps.get(ATTR_modelPackage);
        List<String> modelClasses = dsProps.getList(ATTR_modelClasses);//这里赋值格式： table_name#primaryKey#JavaBean
        
        dsProps.remove(ATTR_modelPackage);
        dsProps.remove(ATTR_modelClasses);
        
        if(Utils.isNotBlank(modelPackage) && modelClasses!=null && modelClasses.size() > 0) {
        	ClassLoader classLoader = AppClassLoader.global();
        	for(String m:modelClasses) {
        		String[] cldesc = m.split("#");
        		//cldesc[0]:table_name , cldesc[2]:class_name
        		if(cldesc.length < 3 || StrKit.isBlank(cldesc[0]) || StrKit.isBlank(cldesc[2])) {
        			continue;
        		}
        		Class<?> clz = ClassUtil.loadClass(classLoader, modelPackage+"."+cldesc[2]); 
        		if(clz == null) {
        			continue;
        		}
        		arp.addMapping(cldesc[0], cldesc[1], (Class<? extends Model<?>>)clz);
        	}
        	
//        	modelPackage = modelPackage.replace('.', '/');
//        	ScanUtil.scan(classLoader,modelPackage, n -> n.endsWith(".class")).forEach(url -> {
//                String clzName = url.substring(0, url.length()-6);
//                clzName = clzName.replace('/', '.');
//                
//                Class<?> clz = ClassUtil.loadClass(classLoader, clzName);
//                if(clz!=null && clz.isAssignableFrom(Model.class)) {
//                	
//                }
//            });

        }
        //方言
        String dialect_str = dsProps.get(ATTR_dialect);
        dsProps.remove(ATTR_dialect);
        buildStyle(arp, dialect_str);
        //cache
        String cache_str = dsProps.get(ATTR_cache);
        dsProps.remove(ATTR_cache);
        buildCache(arp,cache_str);
        //tablebuilder
        String tableBuilder_str = dsProps.get(ATTR_tableBuilder);
        dsProps.remove(ATTR_tableBuilder);
        bulidTableBuilder(arp,tableBuilder_str);
        
        //推到事件中心，用于扩展
        EventBus.publish(arp);
//        arp.start();
        return arp;
    }
    
    private static void bulidTableBuilder(ActiveRecordPlugin arp, String tableBuilder) {
    	if(Utils.isNotBlank(tableBuilder)) {
    		TableBuilder tb = null;
    		if(tableBuilder.indexOf(".")>0) {
    			tb = ClassUtil.tryInstance(tableBuilder);
    		}else {
//    			tableBuilder = tableBuilder.toLowerCase();
    			tb = new TableBuilder();
    		}
    		if(tb!=null) {
    			arp.setTableBuilder(tb);
    		}
    	}
    }
    
    /**
     * 缓存设置
     * @param arp
     * @param cache ICache的实现类
     */
    private static void buildCache(ActiveRecordPlugin arp, String cache) {
    	if(Utils.isNotBlank(cache)) {
    		ICache ic = null;
    		if(cache.indexOf(".")>0) {
    			ic = ClassUtil.tryInstance(cache);
    		}else {
    			cache = cache.toLowerCase();
    			switch(cache) {
    				case "ehcache":
    					ic = new EhCache();
    					break;
    				default:
    					break;
    			}
    		}
    		if(ic!=null) {
    			arp.setCache(ic);
    		}
    	}
    }
    
    /**
     * 设置ActiveRecordPlugin的数据库方言
     * @param arp ActiveRecordPlugin
     * @param dialect 数据库方言
     */
    private static void buildStyle(ActiveRecordPlugin arp, String dialect) {
        if (Utils.isNotEmpty(dialect)) {
            Dialect style = null;

            if (dialect.indexOf(".") > 0) {
                style = ClassUtil.tryInstance(dialect);
            } else {
                dialect = dialect.toLowerCase();

                switch (dialect) {
                    case "oracle":
                        style = new OracleDialect();
                        break;
                    case "mysql":
                        style = new MysqlDialect();
                        break;
                    case "sqlserver":
                        style = new SqlServerDialect();
                        break;
                    case "sqlserver2012":
                        style = new SqlServerDialect();
                        break;
                    case "postgres":
                    case "postgresql":
                    case "pgsql":
                        style = new PostgreSqlDialect();
                        break;
                    case "informix":
                        style = new InformixDialect();
                        break;
                    case "ansi":
                        style = new AnsiSqlDialect();
                        break;
                    case "sqlite":
                        style = new Sqlite3Dialect();
                        break;
                    case "h2":
                        style = new H2Dialect();
                        break;
                    default:
                    	break;
                    

                }
            }

            if (style != null) {
            	arp.setDialect(style);
            }
        }
    }
	
}
